package app.serialport;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.Set;
import java.util.TooManyListenersException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import app.serialport.ByteUtil;
import app.serialport.LongValue;
import gnu.io.NRSerialPort;

/**
 * 
 * @author Darkness
 * @date 2017�?0�?0�?下午4:15:02
 * @version 1.0
 * @since 1.0
 */
public class SerialPort {
	
	public static Set<String> getAvailableSerialPorts() {
		return NRSerialPort.getAvailableSerialPorts();
	}
	
	private SerialPortConfig config;
	
	NRSerialPort serial;
	DataInputStream inputStream;
	DataOutputStream outs;
	DataRecivedListener listener = null;
	
	private CountDownLatch countDownLatch;
	
	private LongValue singleStart = new LongValue();
	
	public SerialPort(SerialPortConfig config) {
		this.config = config;
	
		serial = new NRSerialPort(config.getPort(), config.getBaudRate());
	}
	
	public void addListener(DataRecivedListener listener) {
		this.listener = listener;
	}
	
	long singleCost;
	
	public void onDataRecived(byte[] data, long cost) {
		singleCost = System.currentTimeMillis() - singleStart.get();
		
		responseData = data;
		
		
		if(listener != null) {
			listener.onRecived(data, singleCost);
		} else {
//			System.out.println("cost:"+singleCost+" ms,recived:" + ByteUtil.bytes2HexString(data));	
		}
		
		if(countDownLatch != null) {
			countDownLatch.countDown();
		}
	}
	 
	public boolean isConnected() {
		return serial.isConnected();
	}
	
	public boolean connect() {
		boolean isConnect = serial.connect();
		if(!isConnect) {
			return isConnect;
		}
		
		 inputStream = new DataInputStream(serial.getInputStream());
		 outs = new DataOutputStream(serial.getOutputStream());

		try {
			serial.addEventListener(new SerialReader(inputStream, this));
			serial.notifyOnDataAvailable(true);
		} catch (TooManyListenersException e) {
			throw new RuntimeException(e.getMessage());
		}
		
		return isConnect;
	}
	
	 public void disconnect() {
		serial.disconnect();
	}
	 
	private byte[] responseData;
	
	 public void sendDataSync(String data) {
		 byte[] dataBytes = ByteUtil.hexString2Bytes(data);
		 sendDataToSerialPort(dataBytes);
		 
		 try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	 }
	
	public byte[] sendDataAwait(String data) {
		while(countDownLatch != null) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		countDownLatch = new CountDownLatch(1);
//		System.out.println("command: " + data);
		byte[] dataBytes = ByteUtil.hexString2Bytes(data);
		sendDataToSerialPort(dataBytes);
		 
		try {
			countDownLatch.await(config.getTimeOut(), TimeUnit.MILLISECONDS);
			
			countDownLatch = null;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		return responseData;
	}
	
	Decoder decoder = new Decoder(){

		@Override
		public byte[] decode(byte[] data) {
			return data;
		}};
	
	private void sendDataToSerialPort(byte[] data) {
//		System.out.println(LocalDateTime.now() + " " + ByteUtil.bytes2HexString(data));
		singleStart.set(System.currentTimeMillis());;
		try {
			outs.write(data);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}
	
	public void setDefaultDecoder(Decoder decoder) {
		this.decoder = decoder;
	}
}
